function [ output subjectinfo ] = buildEpochs( subjectinfo, cfg )
%BUILDEPOCHS Epoch data for given subject
%   Loads all applicable datasets for given subject and epochs/saves the result using the definition in cfg.

%% Ensure valid output directory
%============================================
% If the newer Database exists, this pref stuff is not necessary..
if isempty(whos('global','Database'))
    if cfg.Output.AutogenerateFilenames
        if ispref('ECoG', 'Data')
            dataBasepath = getpref('ECoG', 'Data');
        else
            error('Preference does not contain data path, edit and run SetDefaultPreferences.m.');
        end
    else
        dataBasepath = cfg.Output.EpochsetDirectory;
    end
else
    global Database; %#ok<TLEV>
    dataBasepath = Database.Path.EEGLabData;
end
%============================================
ensurePath(dataBasepath);

%% Record the timestamp of the .m file
mfnp = dir([mfilename('fullpath') '.m']);
disp(['Running: ' mfnp.name ' (version ' mfnp.date ')']);
output.mfile = mfnp;

%% Pre-load the datasets
disp('Pre-loading the datasets..');
if isempty(cfg.DatasetFilenames)
    EegDataSet = cell(length(subjectinfo.Datasets),1);
    LoadFilenames = {subjectinfo.Datasets.PreprocessedFilename}';
else
    EegDataSet = cell(length(cfg.DatasetFilenames),1);
    LoadFilenames = cfg.DatasetFilenames;
end
for d = 1:length(EegDataSet)
    % Load..
    EegDataSet{d} = pop_loadset('filename', LoadFilenames{d});
    EegDataSet{d} = eeg_checkset( EegDataSet{d} );
end

%% Create all epoch sets based on the definitions in cfg..
for cCell = fieldnames(cfg.EpochDefinitions)' % For all epoch definitions
    cName = cell2mat(cCell);
%     if ~isstruct(cfg.(cName))
%         continue; % skip this, its a fieldname indicating a setting..
%     end
    disp(['======================> Epoching: ' cfg.EpochDefinitions.(cName).name]);
    output.Datasets.(cName).name = cfg.EpochDefinitions.(cName).name;
    output.Datasets.(cName).taskname = cfg.EpochDefinitions.(cName).name(cfg.EpochDefinitions.(cName).name~=' ');
    output.Datasets.(cName).description = cfg.EpochDefinitions.(cName).description;
    output.Datasets.(cName).datecode = datecode;
    output.Datasets.(cName).configuration = cfg.EpochDefinitions.(cName);
    
    %% Create the epoched datasets..
    FINAL = [];
    for condition = cfg.EpochDefinitions.(cName).condition
        disp('Creating epochs for condition.');
        
        %% Find all datasets that participate..
        xSets = [];
        if condition.include.Sphere
            xSets = [xSets strmatch('Sphere', {subjectinfo.Datasets.Type}, 'exact')']; %#ok<AGROW>
        end
        if condition.include.SphereIntermittend
            xSets = [xSets strmatch('Sphere Intermittend', {subjectinfo.Datasets.Type}, 'exact')']; %#ok<AGROW>
        end
        if condition.include.SphereVoluntary
            xSets = [xSets strmatch('SphereVoluntary', {subjectinfo.Datasets.Type}, 'exact')']; %#ok<AGROW>
        end
        if condition.include.FaceHouse
            xSets = [xSets strmatch('FaceHouse', {subjectinfo.Datasets.Type}, 'exact')']; %#ok<AGROW>
        end
        if isempty(xSets)
            disp(['There is no dataset for ' subjectinfo.Name ' that can be used in this condition, skipping...']);
            continue % There is no sphere data, exit for this subject..
        end
                
        %% Load the datasets and epoch..
        for d = xSets
            % Load..
            EEG = EegDataSet{d};           
            EPOCHS = [];
            % Depending on set function, process the loaded data..
            switch (condition.function)
                case 'selectMarkerLatencies'
                    latencies = selectMarkerLatencies(EEG, condition.functionCfg.markersToFind, ...
                        condition.functionCfg.preBoundary, condition.functionCfg.postBoundary, ...
                        condition.functionCfg.includeStart, condition.functionCfg.includeEnd);
                    if ~isempty(latencies)
                        % Add the events into EEG
                        for index = 1:length(latencies)
                            EEG.event(end+1).type = 'TTTempMMMarker';
                            EEG.event(end).latency = latencies(index);
                            EEG.event(end).urevent = [];
                        end;
                        % Check and sort the events
                        EEG = eeg_checkset(EEG, 'eventconsistency');
                        % Epoch the set
                        EPOCHS = pop_epoch( EEG, {  'TTTempMMMarker' }, [min(cfg.timelimits)/1000  max(cfg.timelimits)/1000], 'newname', 'Epochs', 'epochinfo', 'yes');
                    end
                    % Remove the temporary markers
                    if ~isempty(EPOCHS)
                        EPOCHS = pop_editeventvals(EPOCHS, 'delete', find(strcmp({EPOCHS.event.type}, 'TTTempMMMarker')));
                    end
                case 'pop_epoch'                    
                    % Epoch the set
                    EPOCHS = pop_epoch( EEG, condition.functionCfg.eventTypes, [min(cfg.timelimits)/1000  max(cfg.timelimits)/1000], 'newname', 'Epochs', 'epochinfo', 'yes');
                otherwise
                    warning('The requested epoch function is unknown, skipping...');
                    continue;
            end
            if ~isempty(EPOCHS); % If EPOCHS is empty this means there were no epochs to
                if isempty(FINAL) % If FINAL is still empty, add the first set. Otherwise, merge it with the next set.
                    FINAL = EPOCHS;
                else
                    if EPOCHS.pnts~=FINAL.pnts
                        error(['DATASET POINTS: EPOCHS (' num2str(EEG.pnts) ' points); FINAL (' num2str(FINAL.pnts) ' points);']);
                    end
                    FINAL = pop_mergeset( FINAL, EPOCHS, 0);
                end
            else
                disp(['The dataset ' subjectinfo.Datasets(d).PreprocessedFilename ' produced no epochs.']);
            end;
        end
    end
    
    %% After epoching over all conditions, check to see if there are any epochs
    if isempty(FINAL)
        disp(['None of the datasets for ' subjectinfo.Name ' produced valid epochs.']);
        output.Datasets = rmfield(output.Datasets, cName); % remove the set from the output..
        continue;
    else
        disp(['The datasets for ' subjectinfo.Name ' produced ' int2str(FINAL.trials) ' epochs.']);
        output.Datasets.(cName).nrOfEpochsFound = int2str(FINAL.trials);
    end
    
    %% Store the dataset
    if cfg.Output.AutogenerateFilenames
        output.Datasets.(cName).epochedSetname = [subjectinfo.PrimaryPrefix ' ' cfg.EpochDefinitions.(cName).name ' epochs'];
    else
        output.Datasets.(cName).epochedSetname = [cfg.Output.EpochsetFilenamePrefix ' ' cfg.EpochDefinitions.(cName).name ' epochs'];
    end
    FINAL = pop_editset(FINAL, 'setname', output.Datasets.(cName).epochedSetname);
    FINAL = eeg_checkset( FINAL );
    % Store the information in subjectinfo
    output.Datasets.(cName).dataFilename = [dataBasepath output.Datasets.(cName).epochedSetname '.set'];
    % Save the set to disk
    pop_saveset(FINAL, 'filename', output.Datasets.(cName).dataFilename);
    %     output.(c).EEG = FINAL;
    
end % Now go do the next type of epoch set..

if cfg.Output.StoreInSubjectinfo
    subjectinfo.EpochData = output.Datasets;
end
if cfg.SaveSubjectinfo
    subjectinfo.Save;
end

end

